<!DOCTYPE html>
<html>

  <head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  <title>安卓自定义View进阶-事件分发机制原理</title>
  <meta name="description" content="之前讲解了很多与View绘图相关的知识，你可以在安卓自定义View教程目录中查看到这些文章，如果你理解了这些文章，那么至少2D绘图部分不是难题了，大部分的需求都能满足，但是关于View还有很多知识点，例如 让绘图更加炫酷的Paint，让View动起来的动画，与用户交互的触控事件等一系列内容。本次就带大家简单的了解...">
  <meta name="author" content="GcsSloop">
  <meta name="keywords" content="事件分发,责任链模式, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">
  <meta name="关键字" content="事件分发,责任链模式, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">
  

  <meta name="twitter:card" content="summary">
  <meta name="twitter:title" content="安卓自定义View进阶-事件分发机制原理">
  <meta name="twitter:description" content="之前讲解了很多与View绘图相关的知识，你可以在安卓自定义View教程目录中查看到这些文章，如果你理解了这些文章，那么至少2D绘图部分不是难题了，大部分的需求都能满足，但是关于View还有很多知识点，例如 让绘图更加炫酷的Paint，让View动起来的动画，与用户交互的触控事件等一系列内容。本次就带大家简单的了解...">
  <meta name="twitter:keywords" content="事件分发,责任链模式, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">
  
  <meta property="og:type" content="article">
  <meta property="og:title" content="安卓自定义View进阶-事件分发机制原理">
  <meta property="og:description" content="之前讲解了很多与View绘图相关的知识，你可以在安卓自定义View教程目录中查看到这些文章，如果你理解了这些文章，那么至少2D绘图部分不是难题了，大部分的需求都能满足，但是关于View还有很多知识点，例如 让绘图更加炫酷的Paint，让View动起来的动画，与用户交互的触控事件等一系列内容。本次就带大家简单的了解...">
  <meta name="og:keywords" content="事件分发,责任链模式, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">

  <meta name="theme-color" content="#343434">
  
  <link rel="icon" type="image/png" href="https://raw.githubusercontent.com/GcsSloop/gcssloop.github.io/master/assets/siteinfo/favicon.png" />
  <link href="https://raw.githubusercontent.com/GcsSloop/gcssloop.github.io/master/assets/siteinfo/favicon.png" rel="shortcut icon" type="image/png">
  
  <link href="//netdna.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet">
  <link rel="stylesheet" href="/css/main.css">

  <link rel="canonical" href="http://www.gcssloop.com/customview/dispatch-touchevent-theory">
  <link rel="alternate" type="application/rss+xml" title="GcsSloop" href="http://www.gcssloop.com/feed.xml">
  
  <meta name="google-site-verification" content="Z_g58PkzRAyBMxkqrcDdWrTBK8oOWM-7rUHauhLNF2E" />
  <meta name="baidu-site-verification" content="kUtTXCKaZs" />
  <meta name="baidu-site-verification" content="6DuDv3aaJX" />
  
  <!--阅读次数统计-->
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"> </script>
  
  <!--Fuck Weixin and Baidu-->
  <meta http-equiv="Cache-Control" content="no-transform">
  <meta http-equiv=”Cache-Control” content=”no-siteapp” />
  <meta name="applicable-device" content="pc,mobile">
  <meta name="HandheldFriendly" content="true"/>

  <!-- Google Ad -->
  <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
  <script>
    (adsbygoogle = window.adsbygoogle || []).push({
      google_ad_client: "ca-pub-2767831356529421",
      enable_page_level_ads: true
    });
  </script>

</head>


  <body>

    <span class="mobile btn-mobile-menu">
        <i class="fa fa-list btn-mobile-menu__icon"></i>
        <i class="fa fa-angle-up btn-mobile-close__icon hidden"></i>
    </span>
    
    <header class="panel-cover panel-cover--collapsed" style="background-image: url('/assets/siteinfo/background-cover.jpg')">
  <div class="panel-main">

    <div class="panel-main__inner panel-inverted">
    <div class="panel-main__content">

        <a href="/#blog" title="前往 GcsSloop 的主页" class="blog-button"><img src="/assets/siteinfo/avatar.jpg" width="80" alt="GcsSloop logo" class="panel-cover__logo logo" /></a>
        <h1 class="panel-cover__title panel-title"><a href="/#blog" title="link to homepage for GcsSloop" class="blog-button">GcsSloop</a></h1>

        
        <span class="panel-cover__subtitle panel-subtitle">Just do IT later.</span>
        
        <hr class="panel-cover__divider" />
        <p class="panel-cover__description">嗨，我是 GcsSloop，一名来自2.5次元的魔法师，Android自定义View系列文章作者，非著名程序员。</p>
        <hr class="panel-cover__divider panel-cover__divider--secondary" />
        
        
        <p class="panel-cover__description">欢迎来到我的魔法世界!</p>
        
        
        <div class="navigation-wrapper">
          <div>
            <nav class="cover-navigation cover-navigation--primary">
              <ul class="navigation">
                <li class="navigation__item"><a href="/#blog" title="访问博客" class="blog-button">博客</a></li>
                
                  
                    <li class="navigation__item"><a href="https://github.com/GcsSloop" target="_blank" title="GcsSloop's GitHub">GitHub</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="/timeline" title="博客目录">目录</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="https://xiaozhuanlan.com/u/GcsSloop" target="_blank" title="小专栏">专栏</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="/category/customview" title="自定义View教程目录">自定义控件</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="/friends" title="友链">友链</a></li>
                  
                  
                
              </ul>
            </nav>
          </div>
          
          <div><nav class="cover-navigation navigation--social">
  <ul class="navigation">

  
  <!-- Weibo -->
  <li class="navigation__item">
    <a href="http://weibo.com/GcsSloop" title="@GcsSloop 的微博" target="_blank">
      <i class='social fa fa-weibo'></i>
      <span class="label">Weibo</span>
    </a>
  </li>
  

  
  <!-- Github -->
  <li class="navigation__item">
    <a href="https://github.com/GcsSloop" title="@GcsSloop 的 Github" target="_blank">
      <i class='social fa fa-github'></i>
      <span class="label">Github</span>
    </a>
  </li>
  
  
  
  <!-- Twitter -->
  <li class="navigation__item">
    <a href="http://twitter.com/GcsSloop" title="@GcsSloop" target="_blank">
      <i class='social fa fa-twitter'></i>
      <span class="label">Twitter</span>
    </a>
  </li>
  

    

  

  
  <!-- RSS -->
  <li class="navigation__item">
    <a href="/feed.xml" rel="author" title="RSS" target="_blank">
      <i class='social fa fa-rss'></i>
      <span class="label">RSS</span>
    </a>
  </li>
  

  
  <!-- Email -->
  <li class="navigation__item">
    <a href="mailto:GcsSloop@gmail.com" title="发邮件给我">
      <i class='social fa fa-envelope'></i>
      <span class="label">Email</span>
    </a>
  </li>
  

  
  <!-- Copyright -->
  <li class="navigation__item">
    <a href="http://choosealicense.online" title="选择版权"  target="_blank">
      <i class="social fa fa-copyright"></i>
      <span class="label">版权</span>
    </a>
  </li>
  
  
  </ul>
</nav>
</div>
        </div>
      </div>
    </div>
    
    
    <div class="panel-cover--overlay cover-slate"></div>
    
  </div>
</header>


    <div class="content-wrapper">
        <div class="content-wrapper__inner">
            <article class="post-container post-container--single" itemscope itemtype="http://schema.org/BlogPosting">
  <header class="post-header">
    <div class="post-meta" style="font-size:.8em">
      <time datetime="2016-09-28 00:00:00 +0800" itemprop="datePublished" class="post-meta__date date">2016-09-28</time> &#8226; <span class="post-meta__tags tags">Camera,MotionEvent</span> &#8226; View <span id="busuanzi_value_page_pv"></span> times.
</span>
    </div>
    <h1 class="post-title">安卓自定义View进阶-事件分发机制原理</h1>
  </header>

  <section class="post">
    <p>之前讲解了很多与View绘图相关的知识，你可以在 <a href="http://www.gcssloop.com/customview/CustomViewIndex">安卓自定义View教程目录</a> 中查看到这些文章，如果你理解了这些文章，那么至少2D绘图部分不是难题了，大部分的需求都能满足，但是关于View还有很多知识点，例如: <code class="highlighter-rouge">让绘图更加炫酷的Paint</code>，<code class="highlighter-rouge">让View动起来的动画</code>，<code class="highlighter-rouge">与用户交互的触控事件</code> 等一系列内容。<strong>本次就带大家简单的了解一下与交互息息相关的东西－事件分发原理</strong>。</p>

<blockquote>
  <p>本次魔法小火车的终点站是事件分发，请各位魔法师带好装备，准备登车启程。</p>
</blockquote>

<p><strong>注意：本文中所有源码分析部分均基于 API23(Android 6.0) 版本，由于安卓系统源码改变很多，可能与之前版本有所不同，但基本流程都是一致的。</strong></p>

<h2 id="为什么要有事件分发机制">为什么要有事件分发机制？</h2>

<p><strong>安卓上面的View是树形结构的，View可能会重叠在一起，当我们点击的地方有多个View都可以响应的时候，这个点击事件应该给谁呢？为了解决这一个问题，就有了事件分发机制。</strong></p>

<p>如下图，View是一层一层嵌套的，当手指点击 <code class="highlighter-rouge">View1</code> 的时候，下面的<code class="highlighter-rouge">ViewGroupA</code>、 <code class="highlighter-rouge">RootView</code>  等也是能够响应的，为了确定到底应该是哪个View处理这次点击事件，就需要事件分发机制来帮忙。</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071555.jpg?gcssloop" alt="" /></p>

<h2 id="view的结构">View的结构:</h2>

<p>我们的View是树形结构的，在上一个问题中实例View的结构大致如下:</p>

<p><strong>layout文件:</strong></p>

<pre><code class="language-XML">&lt;com.gcssloop.touchevent.test.RootView
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="300dp"
	android:background="#4E5268"
	android:layout_margin="20dp"
	tools:context="com.gcssloop.touchevent.MainActivity"&gt;

	&lt;com.gcssloop.touchevent.test.ViewGroupA
		android:background="#95C3FA"
		android:layout_width="200dp"
		android:layout_height="200dp"&gt;

		&lt;com.gcssloop.touchevent.test.View1
			android:background="#BDDA66"
			android:layout_width="130dp"
			android:layout_height="130dp"/&gt;

	&lt;/com.gcssloop.touchevent.test.ViewGroupA&gt;

	&lt;com.gcssloop.touchevent.test.View2
		android:layout_alignParentRight="true"
		android:background="#BDDA66"
		android:layout_width="80dp"
		android:layout_height="80dp"/&gt;

&lt;/com.gcssloop.touchevent.test.RootView&gt;
</code></pre>

<p><strong>View结构:</strong></p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071556.jpg?gcssloop" alt="" /></p>

<p><strong>可以看到在上面的View结构中莫名多出来的两个东西，<code class="highlighter-rouge">PhoneWindow</code> 和 <code class="highlighter-rouge">DecorView</code> ，这两个我们并没有在Layout文件中定义过，但是为什么会存在呢？</strong></p>

<blockquote>
  <p>仔细观察上面的 layout 文件，你会发现一个问题，我在 layout 文件中的最顶层 View(Group) 的大小并不是填满父窗体的，留下了大量的空白区域，由于我们的手机屏幕不能透明，所以这些空白区域肯定要显示一些东西，那么应该显示什么呢？</p>

  <p>有过安卓开发经验的都知道，屏幕上没有View遮挡的部分会显示主题的颜色。不仅如此，最上面的一个标题栏也没有在 layout 文件中，这个标题栏又是显示在哪里的呢？</p>

  <p><strong>你没有猜错，这个主题颜色和标题栏等内容就是显示在<code class="highlighter-rouge">DecorView</code>中的。</strong></p>
</blockquote>

<p><strong>现在知道 <code class="highlighter-rouge">DecorView</code> 是干什么的了，那么<code class="highlighter-rouge">PhoneWindow</code> 又有什么作用？</strong></p>

<blockquote>
  <p>要了解 PhoneWindow 是干啥的，首先要了解啥是 Window ，看官方说明：</p>

  <p>Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.</p>

  <p>简单来说，Window是一个抽象类，是所有视图的最顶层容器，视图的外观和行为都归他管，不论是背景显示，标题栏还是事件处理都是他管理的范畴，它其实就像是View界的太上皇(虽然能管的事情看似很多，但是没实权，因为抽象类不能直接使用)。</p>

  <p>而 PhoneWindow 作为 Window 的唯一亲儿子(唯一实现类)，自然就是 View 界的皇帝了，PhoneWindow 的权利可是非常大大，不过对于我们来说用处并不大，因为皇帝平时都是躲在深宫里面的，虽然偶尔用特殊方法能见上一面，但想要完全指挥 PhoneWindow 为你工作是很困难的。</p>

  <p>而上面说的 DecorView 是 PhoneWindow 的一个内部类，其职位相当于小太监，就是跟在 PhoneWindow 身边专业为 PhoneWindow 服务的，除了自己要干活之外，也负责消息的传递，PhoneWindow 的指示通过 DecorView 传递给下面的 View，而下面 View 的信息也通过 DecorView 回传给 PhoneWindow。</p>
</blockquote>

<h2 id="事件分发拦截与消费">事件分发、拦截与消费</h2>

<p>下表省略了 PhoneWidow 和 DecorView。</p>

<blockquote>
  <p><code class="highlighter-rouge">√</code> 表示有该方法。</p>

  <p><code class="highlighter-rouge">X</code> 表示没有该方法。</p>
</blockquote>

<table style="word-break:break-all;">
  <thead>

    <tr>
      <th style="text-align: center">类型</th>
      <th style="text-align: center">相关方法</th>
      <th style="text-align: center">Activity</th>
      <th style="text-align: center">ViewGroup</th>
      <th style="text-align: center">View</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">事件分发</td>
      <td style="text-align: center">dispatchTouchEvent</td>
      <td style="text-align: center">√</td>
      <td style="text-align: center">√</td>
      <td style="text-align: center">√</td>
    </tr>
    <tr>
      <td style="text-align: center">事件拦截</td>
      <td style="text-align: center">onInterceptTouchEvent</td>
      <td style="text-align: center">X</td>
      <td style="text-align: center">√</td>
      <td style="text-align: center">X</td>
    </tr>
    <tr>
      <td style="text-align: center">事件消费</td>
      <td style="text-align: center">onTouchEvent</td>
      <td style="text-align: center">√</td>
      <td style="text-align: center">√</td>
      <td style="text-align: center">√</td>
    </tr>
  </tbody>
</table>

<p>这个三个方法均有一个 boolean(布尔) 类型的返回值，通过返回 true 和 false 来控制事件传递的流程。</p>

<p>PS: 从上表可以看到 Activity 和 View 都是没有事件拦截的，这是因为：</p>

<blockquote>
  <p>Activity 作为原始的事件分发者，如果 Activity 拦截了事件会导致整个屏幕都无法响应事件，这肯定不是我们想要的效果。</p>

  <p>View最为事件传递的最末端，要么消费掉事件，要么不处理进行回传，根本没必要进行事件拦截。</p>
</blockquote>

<h2 id="事件分发流程">事件分发流程</h2>

<p>前面我们了解到了我们的View是树形结构的，基于这样的结构，我们的事件可以进行有序的分发。</p>

<p>事件收集之后最先传递给 Activity， 然后依次向下传递，大致如下：</p>

<div class="highlighter-rouge"><pre class="highlight"><code>Activity －&gt; PhoneWindow －&gt; DecorView －&gt; ViewGroup －&gt; ... －&gt; View
</code></pre>
</div>

<p>这样的事件分发机制逻辑非常清晰，可是，你是否注意到一个问题？如果最后分发到View，如果这个View也没有处理事件怎么办，就这样让事件浪费掉？</p>

<p>当然不会啦，如果没有任何View消费掉事件，那么这个事件会按照反方向回传，最终传回给Activity，如果最后 Activity 也没有处理，本次事件才会被抛弃:</p>

<div class="highlighter-rouge"><pre class="highlight"><code>Activity &lt;－ PhoneWindow &lt;－ DecorView &lt;－ ViewGroup &lt;－ ... &lt;－ View
</code></pre>
</div>

<p><strong>看到这里，我不禁微微一皱眉，这个东西咋看起来那么熟悉呢？再仔细一看，这不就是一个非常经典的<a href="https://zh.wikipedia.org/wiki/%E8%B4%A3%E4%BB%BB%E9%93%BE%E6%A8%A1%E5%BC%8F">责任链模式</a>吗，</strong> 如果我能处理就拦截下来自己干，如果自己不能处理或者不确定就交给责任链中下一个对象。</p>

<p><strong>这种设计是非常精巧的，上层View既可以直接拦截该事件，自己处理，也可以先询问(分发给)子View，如果子View需要就交给子View处理，如果子View不需要还能继续交给上层View处理。既保证了事件的有序性，又非常的灵活。在我第一次将这个逻辑弄清楚的时候，看着这样精妙的设计，简直想欢呼庆贺一下。</strong></p>

<p>其实关于事件传递机制，吴小龙的 <a href="http://wuxiaolong.me/2015/12/19/MotionEvent/">Android事件传递机制分析</a> 一文中的比喻非常有趣，本文也会借鉴一些其中的内容。</p>

<p>先确定几个角色：</p>

<blockquote>
  <p>Activity － 公司大老板</p>

  <p>RootView － 项目经理</p>

  <p>ViewGroupA － 技术小组长</p>

  <p>View1 － 码农小王(公司里唯一的码农)</p>

  <p>View2 － 跑龙套的路人甲，无视即可</p>
</blockquote>

<p><strong>PS：由于 PhoneWindow 和 DecorView 我们无法直接操作，以下所有示例均省略了 PhoneWindow 和 DecorView。</strong></p>

<h3 id="1点击-view1-区域但没有任何-view-消费事件">1.点击 View1 区域但没有任何 View 消费事件</h3>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071555.jpg?gcssloop" alt="" /></p>

<p>当手指在 <code class="highlighter-rouge">View1</code> 区域点击了一下之后，如果所有View都不消耗事件，你就能看到一个完整的事件分发流程，大致如下：</p>

<blockquote>
  <p>红色箭头方向表示事件分发方向。</p>

  <p>绿色箭头方向表示事件回传方向。</p>
</blockquote>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-71558.jpg?gcssloop" alt="" /></p>

<blockquote>
  <p><strong>注意: 上图显示分发流程仅仅是一个示意流程，并不代表实际情况，如果按照实际情况绘制，会导致流程图非常复杂和混乱，在纠结了好久之后做了一个艰难的决定，采用这样一个简化后的流程。</strong></p>

  <p>上面的流程中存在部分不合理内容，请大家选择性接受。</p>

  <ol>
    <li>事件返回时 <code class="highlighter-rouge">dispatchTouchEvent</code> 直接指向了父View的 <code class="highlighter-rouge">onTouchEvent</code> 这一部分是不合理的，实际上它仅仅是给了父View的 <code class="highlighter-rouge">dispatchTouchEvent</code> 一个 false 返回值，父View根据返回值来调用自身的 <code class="highlighter-rouge">onTouchEvent</code>。</li>
    <li>ViewGroup 是根据 <code class="highlighter-rouge">onInterceptTouchEvent</code> 的返回值来确定是调用子View的 <code class="highlighter-rouge">dispatchTouchEvent</code> 还是自身的 <code class="highlighter-rouge">onTouchEvent</code>， 并没有将调用交给 <code class="highlighter-rouge">onInterceptTouchEvent</code>。</li>
    <li>ViewGroup 的事件分发机制伪代码如下，可以看出调用的顺序。</li>
  </ol>

  <div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">dispatchTouchEvent</span><span class="o">(</span><span class="n">MotionEvent</span> <span class="n">ev</span><span class="o">)</span> <span class="o">{</span>
    <span class="kt">boolean</span> <span class="n">result</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>             <span class="c1">// 默认状态为没有消费过</span>

    <span class="k">if</span> <span class="o">(!</span><span class="n">onInterceptTouchEvent</span><span class="o">(</span><span class="n">ev</span><span class="o">))</span> <span class="o">{</span>   <span class="c1">// 如果没有拦截交给子View</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">child</span><span class="o">.</span><span class="na">dispatchTouchEvent</span><span class="o">(</span><span class="n">ev</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="k">if</span> <span class="o">(!</span><span class="n">result</span><span class="o">)</span> <span class="o">{</span>                      <span class="c1">// 如果事件没有被消费,询问自身onTouchEvent</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">onTouchEvent</span><span class="o">(</span><span class="n">ev</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="k">return</span> <span class="n">result</span><span class="o">;</span>
<span class="o">}</span>
</code></pre>
  </div>
</blockquote>

<p><strong>测试:</strong></p>

<blockquote>
  <p><strong>情景：老板: 我看公司最近业务不咋地，准备发展一下电商业务，下周之前做个淘宝出来试试怎么样。</strong></p>

  <p><strong>事件顺序，老板(MainActivity)要做淘宝，这个事件通过各个部门(ViewGroup)一层一层的往下传，传到最底层的时候，码农小王(View1)发现做不了，于是消息又一层一层的回传到老板那里。</strong></p>

  <p>可以看到整个事件传递路线非常有序。从Activity开始，最后回传给Activity结束(由于我们无法操作Phone Window和DecorView，所以没有它们的信息)。</p>
</blockquote>

<div class="highlighter-rouge"><pre class="highlight"><code>MainActivity [老板]: dispatchTouchEvent     经理,我准备发展一下电商业务,下周之前做一个淘宝出来.
RootView     [经理]: dispatchTouchEvent     呼叫技术部,老板要做淘宝,下周上线.
RootView     [经理]: onInterceptTouchEvent  (老板可能疯了,但又不是我做.)
ViewGroupA   [组长]: dispatchTouchEvent     老板要做淘宝,下周上线?
ViewGroupA   [组长]: onInterceptTouchEvent  (看着不太靠谱,先问问小王怎么看)
View1        [码农]: dispatchTouchEvent     做淘宝???
View1        [码农]: onTouchEvent           这个真心做不了啊.
ViewGroupA   [组长]: onTouchEvent           小王说做不了.
RootView     [经理]: onTouchEvent           报告老板, 技术部说做不了.
MainActivity [老板]: onTouchEvent           这么简单都做不了,你们都是干啥的(愤怒).
</code></pre>
</div>

<h3 id="2点击-view1-区域且事件被-view1-消费">2.点击 View1 区域且事件被 View1 消费</h3>

<p>如果事件被View1消费掉了则事件会回传告诉上层View这个事件已经被我解决了，上层View就无需再响应了。</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071559.jpg?gcssloop" alt="" /></p>

<blockquote>
  <p>注意：这张图中的事件回传路径才是正确的路径。</p>
</blockquote>

<p><strong>测试:</strong></p>

<blockquote>
  <p><strong>情景：老板: 我觉得咱们这个app按钮不好看，做的有光泽一点，要让人有一种想点的欲望。</strong></p>

  <p><strong>事件顺序，老板(MainActivity)要做改界面，这个事件通过各个部门(ViewGroup)一层一层的往下传，传到最底层的时候，码农小王(View1)就在按钮上添加了一道光(为啥是小王呢？因为公司没有设计师)。</strong></p>

  <p>可以看出，事件一旦被消费就意味着消息传递的结束，上层View知道了事件已经被消费掉，就不再处理了。</p>
</blockquote>

<div class="highlighter-rouge"><pre class="highlight"><code>MainActivity [老板]: dispatchTouchEvent     把按钮做的好看一点,要有光泽,给人一种点击的欲望.
RootView     [经理]: dispatchTouchEvent     技术部,老板说按钮不好看,要加一道光.
RootView     [经理]: onInterceptTouchEvent  
ViewGroupA   [组长]: dispatchTouchEvent     给按钮加上一道光.
ViewGroupA   [组长]: onInterceptTouchEvent  
View1        [码农]: dispatchTouchEvent     加一道光.
View1        [码农]: onTouchEvent           做好了.
</code></pre>
</div>

<blockquote>
  <p>加一道光：</p>

  <p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071600.jpg?gcssloop" alt="" /></p>
</blockquote>

<h3 id="3点击-view1-区域但事件被-viewgroupa-拦截">3.点击 View1 区域但事件被 ViewGroupA 拦截</h3>

<blockquote>
  <p>上层的View有权拦截事件，不传递给下层View，例如 ListView 滑动的时候，就不会将事件传递给下层的子 View。</p>
</blockquote>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-71601.jpg?gcssloop" alt="" /></p>

<blockquote>
  <p>注意：可以看到，如果上层拦截了事件，下层View将接收不到事件信息。</p>
</blockquote>

<p><strong>测试：</strong></p>

<blockquote>
  <p><strong>情景：老板: 报告一下项目进度。</strong></p>

  <p><strong>事件顺序，老板(MainActivity)要知道项目进度，这个事件通过各个部门(ViewGroup)一层一层的往下传，传到技术组组长(ViewGroupA)的时候，组长(ViewGroupA)上报任务即可。无需告知码农小王(View1)。</strong></p>
</blockquote>

<div class="highlighter-rouge"><pre class="highlight"><code>MainActivity [老板]: dispatchTouchEvent     现在项目做到什么程度了?
RootView     [经理]: dispatchTouchEvent     技术部,你们的app快做完了么?
RootView     [经理]: onInterceptTouchEvent  
ViewGroupA   [组长]: dispatchTouchEvent     项目进度?
ViewGroupA   [组长]: onInterceptTouchEvent  
ViewGroupA   [组长]: onTouchEvent           正在测试,明天就测试完了
</code></pre>
</div>

<h3 id="其他情况">其他情况</h3>

<p>事件分发机制设计到到情形非常多，这里就不一一列举了，记住以下几条原则就行了。</p>

<ul>
  <li>1.如果事件被消费，就意味着事件信息传递终止。</li>
  <li>2.如果事件一直没有被消费，最后会传给Activity，如果Activity也不需要就被抛弃。</li>
  <li>3.判断事件是否被消费是根据返回值，而不是根据你是否使用了事件。</li>
</ul>

<h4 id="文中测试用的源码下载"><a href="https://raw.githubusercontent.com/GcsSloop/AndroidNote/master/CustomView/Demo/dispatchTouchEventDemo.zip">文中测试用的源码下载</a></h4>

<h2 id="总结">总结</h2>

<p>View的事件分发机制实际上就是一个非常经典的责任链模式，如果你了解责任链模式，那么事件分发对你来说并不是什么难题，如果你不了解责任链模式，刚好借此机会学习一下啦。</p>

<blockquote>
  <p><strong>责任链模式：</strong></p>

  <p>当有多个对象均可以处理同一请求的时候，将这些对象串联成一条链，并沿着这条链传递改请求，直到有对象处理它为止。</p>
</blockquote>

<p>Android 中事件分发机制原理虽然非常简单，但由于实际场景非常复杂，一旦具体到某个场景中变得很麻烦，而本文仅仅是带你简单的了解一下事件分发机制，更详细的内容和具体的一些特殊情形处理会在后续文章中进行讲解。</p>

<p>由于个人水平有限，文章中可能会出现错误，如果你觉得哪一部分有错误，或者发现了错别字等内容，欢迎在评论区告诉我，另外，据说关注<a href="http://weibo.com/GcsSloop">作者微博</a>不仅能第一时间收到新文章消息，还能变帅哦。</p>

<h2 id="参考资料">参考资料</h2>

<p><a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/Activity.java">Activity</a><br />
<a href="https://android.googlesource.com/platform/frameworks/base/+/696cba573e651b0e4f18a4718627c8ccecb3bda0/policy/src/com/android/internal/policy/impl/PhoneWindow.java">PhoneWindow</a><br />
<a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java">ViewGroup</a><br />
<a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/View.java">View</a><br />
<a href="http://wuxiaolong.me/2015/12/19/MotionEvent/">Android事件传递机制分析</a><br />
<a href="http://anany.me/2015/11/08/touchevent/">Android ViewGroup/View 事件分发机制详解</a><br />
<a href="http://blog.csdn.net/guolin_blog/article/details/9097463"> Android事件分发机制完全解析，带你从源码的角度彻底理解(上)</a><br />
<a href="http://blog.csdn.net/sinyu890807/article/details/9153747"> Android事件分发机制完全解析，带你从源码的角度彻底理解(下)</a><br />
<a href="http://www.idtkm.com/customview/customview11/">更简单的学习Android事件分发</a><br />
《安卓开发艺术探索》</p>

<h2 id="about-me">About Me</h2>

<h3 id="作者微博-gcssloop">作者微博: <a href="http://weibo.com/GcsSloop" target="_blank">@GcsSloop</a></h3>

<p><a href="http://www.gcssloop.com/info/about" target="_blank"><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/gcs_banner.jpg?gcssloop" width="300" style="display:inline;" /></a></p>


    <hr>
  </section>
</article>

<!--广告-->

<!--
<div>
<a href="https://m.aliyun.com/act/team1111/?spm=5176.11533457.1089570.5.424777e3AF8WOJ&userCode=hn5smxtw#/" target="_blank"><img src="/assets/aliyun/1111-980-120.jpg" width="100%"></a>
</div>
-->
<!--捐赠晶石-->
<section class="contribute">
    <script type="text/javascript"> 
      function toggle() {
        var con = document.getElementById("contribute");
        if (con.style.display == "none") {
          con.style.display = "block";
        } else {
          con.style.display = "none";
        }
      }
    </script> 
    <blockquote style="background-color:#F5F5F5; padding: 10px 20px 20px 10px; margin:0px" >
      <h4> 如果你觉得我的文章对你有帮助的话，欢迎赞助一些服务器费用! </h4>
      <p></p>
      <a id=“btn-toggle-contribute” class="btn-contribute" onclick="toggle()" >¥ 点击赞助</a>
      <br>
      <div id="contribute" style="display:none;">
        <p align="center" >
        <img src="/assets/images/wechat.png" alt="微信">
        <img src="/assets/images/alipay.png" alt="支付宝">
        </p>
        <p align="left" >
          <b>感谢所有支持我的魔法师，所有支持过我的魔法师都可以通过微信(GcsSloop)联系我，获赠我的付费专栏！</b>
          <!--
          <a href="/contribute">点击这里查看捐赠者名单。</a>
          -->
        </p>
      </div>
    </blockquote>
</section>
<div>
  <h2>欢迎关注我的微信公众号</h2>
  <img src="/assets/images/banner.jpg" width="100%">
</div>

<!--阅读更多-->
<section class="read-more">
  
  
  <div class="read-more-item">
    <span class="read-more-item-dim">最近的文章</span>
    <h2 class="post-list__post-title post-title"><a href="/python/tinypng-voice" title="link to 让脚本说话"厉害了我的哥"">让脚本说话"厉害了我的哥"</a></h2>
    <p class="excerpt">前段时间发现 Mac 上一个有趣的方法可以让系统说话。例如在终端输入：say "厉害了我的哥"于是把我自己经常使用的图片压缩脚本升了一下级，人工(智能)语音提示，开启装逼新时代!  本次升级进行...&hellip;</p>
    <div class="post-list__meta">
      <time datetime="2016-10-10 00:00:00 +0800" class="post-list__meta--date date">2016-10-10</time> &#8226; <span class="post-list__meta--tags tags">Python</span>
      <br/><br/>
      <a style="float:none; margin:0 auto;" class="btn-border-small" href=/python/tinypng-voice>继续阅读</a></div>
   </div>
   
   
   
   
   <div class="read-more-item">
       <span class="read-more-item-dim">更早的文章</span>
       <h2 class="post-list__post-title post-title"><a href="/tips/choose-license" title="link to 程序员不可不知的版权协议">程序员不可不知的版权协议</a></h2>
       <p class="excerpt">前一段时间知乎上关于版权问题的讨论有不少，例如这个 新浪微博上的「知乎大神」是谁？涉嫌侵权吗？, 而且最近喜马拉雅FM也因为背景音乐版权问题导致很多栏目被下架和推迟更新，而我作为一个喜欢分享的魔...&hellip;</p>
       <div class="post-list__meta">
          <time datetime="2016-09-20 00:00:00 +0800" class="post-list__meta--date date">2016-09-20</time> &#8226; <span class="post-list__meta--tags tags">Tips</span>
          <br/><br/>
          <a style="float:none; margin:0 auto;" class="btn-border-small" href=/tips/choose-license>继续阅读</a>
       </div>
   </div>
   
</section>

<!--网易云跟帖-->
<!--
<div id="cloud-tie-wrapper" class="cloud-tie-wrapper"></div>
<script src="https://img1.cache.netease.com/f2e/tie/yun/sdk/loader.js"></script>
<script>
var cloudTieConfig = {
  url: document.location.href, 
  sourceId: "",
  productKey: "a85dba2840134721a7b69a15b2e0f217",
  target: "cloud-tie-wrapper"
};
var yunManualLoad = true;
Tie.loader("aHR0cHM6Ly9hcGkuZ2VudGllLjE2My5jb20vcGMvbGl2ZXNjcmlwdC5odG1s", true);
</script>
-->

<style type="text/css">
.isso-comment > div.avatar {
    border: 0px;
    box-shadow: none;
    display: block;
    float: left;
    width: 7%;
    margin: 3px 15px 0 0;
}
.isso-postbox > .form-wrapper > .auth-section .post-action > input {
    border-radius: 6px;
    padding: 6px;
    padding-left: 16px;
    padding-right: 16px;
    border: 1px solid #CCC;
    background-color: #D58D44;
    cursor: pointer;
    outline: 0;
    color: #fff;
    size: 10;
    line-height: 1.4em;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.isso-postbox > .form-wrapper > .auth-section .post-action > input:hover {
    background-color: #272822;
}
.isso-postbox > .form-wrapper > .auth-section .post-action > input:active {
    background-color: #986530;
}
</style>

<section id="isso-thread"></section>

<script data-isso="//47.52.58.34:1234/"
        data-isso-css="true"
        data-isso-lang="zh"
        data-isso-reply-to-self="false"
        data-isso-require-author="false"
        data-isso-require-email="false"
        data-isso-max-comments-top="10"
        data-isso-max-comments-nested="5"
        data-isso-reveal-on-click="5"
        data-isso-avatar="true"
        data-isso-avatar-bg="#f0f0f0"
        data-isso-avatar-fg="#9abf88 #5698c4 #e279a3 #9163b6 ..."
        data-isso-vote="true"
        data-vote-levels=""
        src="//47.52.58.34:1234/js/embed.min.js">
        </script>

<!--
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
-->
<!-- OneV's Den -->
<!--
<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-3324997515191619"
     data-ad-slot="9170309685"
     data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
-->

            <section class="footer">
    <footer>
    	<span class="footer__copyright">本站点采用<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh" target="_blank">知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议</a></span>
        <span class="footer__copyright">本站由 <a href="http://www.GcsSloop.com">@GcsSloop</a> 创建，采用 <a href="https://github.com/GcsSloop/Gcs-Vno-Jekyll" target="_blank">Gcs-Vno-Jekyll</a> 作为主题。<span id="busuanzi_container_site_pv"> 总访问量 <span id="busuanzi_value_site_pv"></span> 次</span> - &copy; 2019</span>
        <span class="footer__sitemap, footer__copyright"><a href="http://www.gcssloop.com/sitemap.xml" target="_blank">Site Map</a>
        <a href="http://www.gcssloop.com/vip" target="_blank">vip</a></span>
    </footer>
</section>

        </div>
    </div>
    
    <script type="text/javascript" src="//code.jquery.com/jquery-1.11.3.min.js"></script>

<script type="text/javascript" src="/js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

<script type="text/javascript" src="/js/main.js"></script>

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-82493667-1', 'auto');
  ga('send', 'pageview');

</script>

    
  </body>

</html>
